package com.lollipop.synthential.java8.lambda.stream;


import org.apache.commons.collections4.ListUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author: J.K
 * @Date: 2021-04-10 15:59
 * @Description: 遍历/匹配(foreach/find/match)
 */
public class StreamDemo2 {
    public static void main(String[] args) {

        List<Integer> integerList = Arrays.asList(1, 8, 3, 6, 2, 9, 10, 6);
        List<Integer> integerList2 = Arrays.asList(1, 6, 2, 9, 6);

        List<String> stringList = Arrays.asList("adnm,lin", "admmt,dfd", "pot", "xbangd", "weoujgsd");

        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Tom", 8900, 20, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 28, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 26, "female", "New York"));
        personList.add(new Person("Owen", 9500, 29, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));

        // 遍历/匹配（foreach/find/match）->
        // 遍历输出符合条件的元素
        integerList.stream().filter(x -> x > 2).forEach(System.out::println);

        // 筛选（filter）->
        // 匹配第一个
        Optional<Integer> first = integerList.stream().filter(x -> x > 6).findFirst();
        System.out.println("匹配第一个:" + first.get());

        // 匹配任意一个（适用于并行流）
        Optional<Integer> any = integerList.stream().filter(x -> x > 6).findAny();
        System.out.println("匹配任意一个:" + any.get());

        // 是否存在小于3的值
        boolean b = integerList.stream().anyMatch(x -> x < 3);
        System.out.println("是否存在小于3的值:" + b);

        // 工资高于8k的员工姓名
        List<String> filterList = personList.stream().filter(x -> x.getSalary() > 8000).map(Person::getName).collect(Collectors.toList());
        System.out.println("工资高于8k的员工姓名:" + filterList);

        // 聚合（max/min/count）->
        // 获取String集合中最长的元素
        Optional<String> max = stringList.stream().max(Comparator.comparing(String::length));
        System.out.println("最长的字符串：" + max.get());

        // 自然排序
        Optional<Integer> max1 = integerList.stream().max(Integer::compareTo);
        System.out.println("自然排序的最大值：" + max1.get());

        // 自定义排序
        Optional<Integer> max2 = integerList.stream().max((o1, o2) -> o2.compareTo(o1));
        System.out.println("自定义排序的最大值：" + max2.get());

        // 获取员工工资最高的人
        Optional<Person> max3 = personList.stream().max(Comparator.comparingInt(Person::getSalary));
        System.out.println("员工工资最大值：" + max3.get().getSalary());

        // 计算集合中大于6的元素的个数
        long count = integerList.stream().filter(x -> x > 6).count();
        System.out.println("integerList中大于6的元素的个数：" + count);

        /**
         * 映射（map/flatMap）映射，可以将一个流的元素按照一定的映射规则映射到另一个流中。
         * 分为map和flatMap：
         * map：接收一个函数作为参数，该函数会被应用到每个元素上，并将其映射成一个新的元素。
         * flatMap：接收一个函数作为参数，将流中的每个值都换成另一个流，然后把所有流连接成一个流。
         */
        // 每个元素+3
        List<Integer> integerCollect = integerList.stream().map(x -> x + 2).collect(Collectors.toList());
        System.out.println("每个元素+3：" + integerCollect);

        // 每个元素大写
        List<String> stringCollect = stringList.stream().map(String::toUpperCase).collect(Collectors.toList());
        System.out.println("每个元素大写：" + stringCollect);

        // 将员工的薪资全部增加1000
        // 不改变原来员工集合的方式
        List<Person> personListNew = personList.stream().map(person -> {
            Person psersonNew = new Person(person.getName(), 0, 0, null, null);
            psersonNew.setSalary(person.getSalary() + 1000);
            return psersonNew;
        }).collect(Collectors.toList());
        System.out.println("一次改动前：" + personList.get(0).getName() + "-->" + personList.get(0).getSalary());
        System.out.println("一次改动后：" + personListNew.get(0).getName() + "-->" + personListNew.get(0).getSalary());

        // 改变原来员工集合的方式
        List<Person> personListNew2 = personList.stream().map(person -> {
            person.setSalary(person.getSalary() + 1000);
            return person;
        }).collect(Collectors.toList());
        System.out.println("二次改动前：" + personList.get(0).getName() + "-->" + personList.get(0).getSalary());
        System.out.println("二次改动后：" + personListNew2.get(0).getName() + "-->" + personListNew2.get(0).getSalary());

        // 将两个字符数组合并成一个新的字符数组
        List<Object> combineList = stringList.stream().flatMap(s -> {
            String[] split = s.split(",");
            Stream<String> stream = Arrays.stream(split);
            return stream;
        }).collect(Collectors.toList());
        System.out.println("处理前的集合：" + stringList);
        System.out.println("处理后的集合：" + combineList);


        /**
         * 归约（reduce），也称缩减，顾名思义，是把一个流缩减成一个值，能实现对集合求和、求乘积和求最值操作。
         */
        // 求和方式1
        Optional<Integer> sum1 = integerList.stream().reduce((x, y) -> x + y);

        // 求和方式2
        Optional<Integer> sum2 = integerList.stream().reduce(Integer::sum);

        // 求和方式3
        Integer sum3 = integerList.stream().reduce(0, Integer::sum);
        System.out.println("integerList求和：" + sum1.get() + "," + sum2.get() + "," + sum3);

        // 求乘积
        Optional<Integer> product = integerList.stream().reduce((x, y) -> x * y);
        System.out.println("integerList求积：" + product.get());

        // 求最大值方式1
        Optional<Integer> maximum1 = integerList.stream().reduce((x, y) -> x > y ? x : y);

        // 求最大值方式2
        Integer maximum2 = integerList.stream().reduce(1, Integer::max);
        System.out.println("integerList最大值：" + maximum1.get() + "," + maximum2);

        // 求所有员工的工资之和和最高工资
        // 求工资之和方式1
        Optional<Integer> sumSalary1 = personList.stream().map(Person::getSalary).reduce(Integer::sum);

        // 求工资之和方式2
        Integer sumSalary2 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(), (x, y) -> x + y);

        // 求工资之和方式3
        Integer sumSalary3 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);

        // 求最高工资方式1
        Integer maxSalary1 = personList.stream().reduce(0, (maximum, p) -> maximum > p.getSalary() ? maximum : p.getSalary(), Integer::max);

        // 求最高工资方式2
        Integer maxSalary2 = personList.stream().reduce(0, (maximum, p) -> maximum > p.getSalary() ? maximum : p.getSalary(), (x, y) -> x > y ? x : y);
        System.out.println("工资之和：" + sumSalary1.get() + "," + sumSalary2 + "," + sumSalary3);
        System.out.println("最高工资：" + maxSalary1 + "," + maxSalary2);


        /**
         * 收集（collect） collect，收集，可以说是内容最繁多、功能最丰富的部分了。
         * 从字面上去理解，就是把一个流收集起来，最终可以是收集成一个值也可以收集成一个新的集合。
         * collect主要依赖java.util.stream.Collectors类内置的静态方法。
         */
        // 归集（toList/toSet/toMap/toCollection/toConcurrentMap...）->
        List<Integer> collectList = integerList.stream().filter(x -> x > 2).collect(Collectors.toList());
        Set<Integer> collectSet = integerList.stream().filter(x -> x > 2).collect(Collectors.toSet());

        Map<String, Integer> collectMap = personList.stream().filter(p -> p.getSalary() > 8000).collect(Collectors.toMap(Person::getName, Person::getSalary));
        System.out.println("toList:" + collectList);
        System.out.println("toSet:" + collectSet);
        System.out.println("toMap:" + collectMap);


        // 统计（count/averaging）->
        /**
         * Collectors提供了一系列用于数据统计的静态方法：
         *
         * 计数：count
         * 平均值：averagingInt、averagingLong、averagingDouble
         * 最值：maxBy、minBy
         * 求和：summingInt、summingLong、summingDouble
         * 统计以上所有：summarizingInt、summarizingLong、summarizingDouble
         */
        // 统计员工人数、平均工资、工资总额、最高工资
        // 求总数
        Long personCount = personList.stream().collect(Collectors.counting());

        // 求平均工资
        Double collectAverageSalary = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));

        // 求最高工资
        Optional<Integer> collectMaxSalary = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));

        // 求工资之和
        IntSummaryStatistics collectSumSalary = personList.stream().collect(Collectors.summarizingInt(Person::getSalary));

        DoubleSummaryStatistics doubleSummaryStatistics = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));

        System.out.println("员工总数：" + personCount);
        System.out.println("员工平均工资：" + collectAverageSalary);
        System.out.println("员工最高工资：" + collectMaxSalary);
        System.out.println("员工工资总和：" + collectSumSalary);
        System.out.println("员工工资所有统计：" + doubleSummaryStatistics);


        // 分组（partitioningBy/groupingBy）
        /**
         * 分区：将stream按条件分为两个Map，比如员工按薪资是否高于8k分为两部分
         * 分组：将集合分为多个Map，比如员工按性别分组。有单级分组和多级分组
         */
        // 将员工按薪资是否高于8k分组
        Map<Boolean, List<Person>> salaryPart = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
        // 将员工按性别分组 x -> x.getSex() 和 Person::getSex()效果一样
        Map<String, List<Person>> sexGroup = personList.stream().collect(Collectors.groupingBy(Person::getSex));
        // 将员工先按性别分组，再按地区分组
        Map<String, Map<String, List<Person>>> ComprehensiveGroup = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));

//        System.out.println("员工按薪资是否大于8000分组情况：" + salaryPart);
//        System.out.println("员工按性别分组情况：" + sexGroup);
//        System.out.println("员工按性别、地区：" + ComprehensiveGroup);


        // 接合（joining） joining可以将stream中的元素用特定的连接符（没有则直接连接）连接成一个字符串
        // 所有员工的姓名
        String names = personList.stream().map(Person::getName).collect(Collectors.joining(","));
        System.out.println("所有员工的姓名：" + names);

        // 归约（reducing） Collectors类提供的reducing方法，相比于stream本身的reduce方法增加了对自定义归约的支持
        // Collects类的reduce 每个员工减去起征点后的薪资之和
        Integer collectReduceSum = personList.stream().collect(Collectors.reducing(0, Person::getSalary, (x, y) -> (x + y - 5000)));
        // stream的reduce
        Optional<Integer> streamReduceSum = personList.stream().map(Person::getSalary).reduce(Integer::sum);
        System.out.println("员工扣税薪资总和：" + collectReduceSum);
        System.out.println("员工薪资总和：" + streamReduceSum);

        // 排序（sorted）
        /**
         * sorted，中间操作
         * 两种排序：1.sorted()：自然排序，流中元素需实现Comparable接口   2.sorted(Comparator com):Comparator排序器自定义排序
         */
        // 按工资升序排序（自然排序）
        List<String> salarySortedList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName).collect(Collectors.toList());
        // 按工资倒序排序
        List<String> salaryRevesedSortList = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed()).map(Person::getName).collect(Collectors.toList());

        // 先按工资再按年龄升序排序
        List<String> comprehensiveSortList = personList.stream().sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName).collect(Collectors.toList());
        // 先按工资再按年龄自定义排序（降序）
        List<String> reversedComprehensiveSortList = personList.stream().sorted((p1, p2) -> {
            if (p1.getSalary() == p2.getSalary()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getSalary() - p1.getSalary();
            }
        }).map(Person::getName).collect(Collectors.toList());

        System.out.println("按工资升序排序：" + salarySortedList);
        System.out.println("按工资降序排序：" + salaryRevesedSortList);
        System.out.println("先按工资再按年龄升序排序：" + comprehensiveSortList);
        System.out.println("先按工资再按年龄自定义降序排序：" + reversedComprehensiveSortList);


        // 提取/组合 流可以进行合并、去重、限制、跳过等操作
        String[] arr1 = {"a", "b", "c", "d"};
        String[] arr2 = {"a", "b", "c", "d", "e", "a"};

        // 数组转流
        Stream<String> stream1 = Stream.of(arr1);
        Stream<String> stream2 = Stream.of(arr2);

        // concat合并两个流，distinct 去重
        List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());

        // limit：从流中获取前n个数据
        List<Integer> limitList = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());

        // skip：跳过前n个数据
        List<Integer> skipLimitList = Stream.iterate(1, x -> x + 2).skip(2).limit(10).collect(Collectors.toList());

        System.out.println("合并流：" + newList);
        System.out.println("limit：" + limitList);
        System.out.println("skip：" + skipLimitList);


        System.out.println("-------------------------------------------List差集、交集、并集、去重并集-------------------------------------------------------------------------");
        System.out.println("-------------方法1：使用Java8 Stream----------------");
        // 方法1：使用Java8 Stream
        // https://www.cnblogs.com/lyrb/p/12923615.html
        // list差集:integerList-integerList2
        List<Integer> reduce = integerList.stream().filter(item -> !integerList2.contains(item)).collect(Collectors.toList());
        System.out.println("----------差集：-----------");
        reduce.parallelStream().forEach(System.out::println);

        // list交集
        List<Integer> intersection = integerList.stream().filter(item -> integerList2.contains(item)).collect(Collectors.toList());
        System.out.println("----------交集：-----------");
        intersection.parallelStream().forEach(System.out::println);

        // list并集
        List<Integer> integerStreamList1 = integerList.parallelStream().collect(Collectors.toList());
        List<Integer> integerStreamList2 = integerList2.parallelStream().collect(Collectors.toList());
        integerStreamList1.addAll(integerStreamList2);
        System.out.println("----------并集：-----------");
        integerStreamList1.parallelStream().forEachOrdered(System.out::println);

        // 去重并集
        List<Integer> distinctStreamList = integerStreamList1.stream().distinct().collect(Collectors.toList());
        System.out.println("----------去重并集：-----------");
        distinctStreamList.parallelStream().forEachOrdered(System.out::println);

        System.out.println("-------------方法2：使用org.apache.commons.collections4.ListUtils第三方工具库----------------");
        // 方法2：使用org.apache.commons.collections4.ListUtils第三方工具库
        // List差集
        List<Integer> reduceList = ListUtils.removeAll(integerList, integerList);
        List<Integer> reduceList2 = ListUtils.subtract(integerList, integerList);

        // List交集
        List<Integer> intersectionList = ListUtils.retainAll(integerList, integerList2);
        List<Integer> intersectionList2 = ListUtils.intersection(integerList, integerList2);

        // List并集
        List<Integer> sumList = ListUtils.sum(integerList, integerList2);

        System.out.println("-------------方法3：使用removeAll----------------");
        // 方法3：使用removeAll，此方法不适用于Arrays.asList转换的List，需要new一个新的List(不推荐用)
        // list差集 newIntegerList-newIntegerList2
        ArrayList<Integer> newIntegerList = new ArrayList<>(integerList);
        ArrayList<Integer> newIntegerList2 = new ArrayList<>(integerList2);
        newIntegerList.removeAll(newIntegerList2);
    }
}
